package io.github.wangtisnruipopo.carscore.types;

import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.IdUtil;
import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;

import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

public abstract class AbsRound<
        D extends AbsDecorValue,
        V extends AbsCardValue,
        C extends AbsSimpleCard<D, V>,
        P extends AbsPlayer<D, V, C>,
        R extends AbsPlayCardsRecord<D, V, ? extends AbsPlayCardsType<C>, C, P>>
        implements IRound<D, V, C, P, R>, IPlayEnd, IAppointPlay {
    @Getter
    @Setter
    protected int index;
    @Getter
    @Setter
    protected P currentActionPlayer;
    protected int currentActionPlayerIndex;
    @Getter
    @Setter
    protected List<P> participant;
    @Getter
    protected List<R> records;
    @Setter
    protected IEnd roundEnd;
    protected Class<R> roundType;
    /**
     * <h2>出牌许可证</h2>
     * <p>每轮到一位玩家出牌，就会生成一个新的许可证</p>
     * <p>用于判断是否是该玩家出牌，防止前端乱传参数攻击服务器</p>
     */
    protected String playLicence;
    protected Timer timer;

    {
        this.records = new ArrayList<>();
        this.currentActionPlayerIndex = 0;
    }

    @Override
    @SuppressWarnings("unchecked")
    public void afterCreate() {
        this.roundType = (Class<R>) ClassUtil.getTypeArgument(this.getClass(), 4);
        // 回合创建完成后，设置当前出牌的玩家
        this.created();
    }

    @Override
    public void next() {
        this.currentActionPlayer = this.participant.get(this.currentActionPlayerIndex);
        this.currentActionPlayer.setPlayEnd(this);
        // 创建计时器
        timer = new Timer();
        // 执行计时器
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                // 时间到，通知玩家强制出牌
            }
        }, this.timerDelay());
        // 通知玩家出牌
        // 生成出牌许可证
//        this.playLicence = IdUtil.fastSimpleUUID();
//        this.currentActionPlayer.notifyPlay();
        this.appoint(this.currentActionPlayerIndex);
    }

    @Override
    public void playEnd(String licence) {
        // 如果出牌许可证和给予当前出牌者的许可证不同，则跳过本次操作
        if (!licence.equals(this.playLicence)) return;
        // 定时器停止计时
        this.timer.cancel();
        this.currentActionPlayerIndex++;
        if (this.currentActionPlayerIndex >= this.participant.size()) {
            // 本次回合结束
            // 通知游戏管理器进行下一回合的创建
            this.roundEnd.end();
        }
        // 下一位玩家出牌
        if (this.nextPlayer()) {
            this.next();
        }
    }

    @Override
    public void appoint(int index) {
        P p = this.participant.get(index);
        p.setPlayEnd(this);
        p.setAppointPlay(this);
        p.setLicence(this.createLicence());
        R r = null;
        for (int i = this.getRecords().size() - 1; i >= 0; i--) {
            r = this.getRecords().get(i);
            if (!r.isSkip()) break;
            r = null;
        }
        p.notifyPlay(this.timerDelay(), r);
    }

    @Override
    @SuppressWarnings("unchecked")
    public void addRecord(IPlayCardsRecord<?, ?, ?, ?, ?> record) {
        this.records.add((R) record);
    }

    @SneakyThrows
    @Override
    @SuppressWarnings("unchecked")
    public <CS extends ISimpleCard<?, ?>, PS extends IPlayer<?, ?, ?>, TS extends IPlayCardsType<CS>>
    void addRecord(List<CS> cards, PS sourcePlayer, PS targetPlayer, TS type, boolean skip) {
        R r = this.roundType.getDeclaredConstructor().newInstance();
        r.setSkip(skip);
        r.setPlayer((P) sourcePlayer);
        r.setTargetPlayer((P) targetPlayer);
        r.setCards((List<C>) cards);
        this.addRecord(r);
    }

    private String createLicence() {
        StringBuffer res = new StringBuffer(IdUtil.fastSimpleUUID());
        this.afterCreateLicence(res);
        return res.toString();
    }

    public abstract void created();
    /**
     * <h2>获取当前计时器延迟执行的毫秒数</h2>
     *
     * @return 毫秒值
     */
    public abstract long timerDelay();
    public abstract boolean nextPlayer();
    public abstract void afterCreateLicence(StringBuffer licence);
}
